package ca.cbc.gis.controller;

import ca.cbc.gis.json.MatchingPois;
import ca.cbc.gis.services.GISService;
import ca.cbc.gis.util.GISResponseBuilder;
import ca.cbc.giscommon.entity.Category;
import ca.cbc.giscommon.entity.Content;
import ca.cbc.giscommon.entity.Event;
import ca.cbc.giscommon.entity.EventSubcategory;
import ca.cbc.giscommon.entity.Shape;
import ca.cbc.giscommon.entity.Source;
import ca.cbc.giscommon.entity.Story;
import ca.cbc.giscommon.entity.Tweet;
import ca.cbc.giscommon.entity.TwitterUser;
import ca.cbc.giscommon.entity.Zone;
import ca.cbc.giscommon.util.GeometryUtil;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.vividsolutions.jts.geom.Geometry;

import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;

import java.util.ArrayList;
import java.util.List;

@Path("/")
@Produces("application/json")
public class GISRestService {
    private static Logger logger = LoggerFactory.getLogger(GISRestService.class);
    private final static String DEFAULT_LIMIT = "0";
    private GISService service;

    public GISRestService() {
        service = new GISService();
    }

    @GET
    @Path("/pois")
    public Response getPois(@QueryParam("isAscending") @DefaultValue("false") boolean isAscending,
                            @QueryParam("limit") @DefaultValue(DEFAULT_LIMIT) int limit,
                            @QueryParam("offset") @DefaultValue("0") int offset,
                            @QueryParam("zoneIds") @DefaultValue("") String zoneIds,
                            @QueryParam("ne") @DefaultValue("") String ne,
                            @QueryParam("sw") @DefaultValue("") String sw,
                            @QueryParam("categoryIds") @DefaultValue("") String categoryIds,
                            @QueryParam("eventSubcategoryIds") @DefaultValue("") String eventSubcategoryIds,
                            @QueryParam("shapeTypeIds") @DefaultValue("") String shapeTypeIds,
                            @QueryParam("startTime") @DefaultValue("0") long startTime,
                            @QueryParam("endTime") @DefaultValue("0") long endTime,
                            @QueryParam("includeAll") @DefaultValue("false") boolean includeAll,
                            @QueryParam("isReturnStory") @DefaultValue("true") boolean isReturnStory,
                            @QueryParam("isReturnContent") @DefaultValue("true") boolean isReturnContent,
                            @QueryParam("isReturnEvent") @DefaultValue("true") boolean isReturnEvent,
                            @QueryParam("isReturnTweet") @DefaultValue("true") boolean isReturnTweet,
                            @QueryParam("isReturnShape") @DefaultValue("false") boolean isReturnShape) {
        logger.debug("****getPois*Param zoneIds:{}, isAscending:{}", zoneIds, isAscending);
        logger.debug("****Param limit:{}, offset:{}", limit, offset);
        Geometry bbox = GeometryUtil.instance.parseEnvelope(ne, sw);
        List<Story> stories = null;
        List<Content> contents = null;
        List<Shape> shapes = null;
        List<Event> events = null;
        List<Tweet> tweets = null;
        if (isReturnStory) {
            stories = service.getStories(zoneIds, bbox, categoryIds, startTime, endTime, "last_updated", isAscending, limit, offset, includeAll);
        }
        if (isReturnContent) {
            contents = service.getContents(zoneIds, bbox, categoryIds, startTime, endTime, "pubdate", isAscending, limit, offset);
        }
        if (isReturnEvent) {
            events = service.getEvents(zoneIds, bbox, categoryIds, eventSubcategoryIds, startTime, endTime, "pubdate", isAscending, limit, offset);
        }
        if (isReturnShape) {
            shapes = service.getShapes(zoneIds, bbox, shapeTypeIds, categoryIds, "id", isAscending, limit, offset);
        }
        if (isReturnTweet) {
            tweets = service.getTweets(zoneIds, bbox, categoryIds, startTime, endTime, "createdat", isAscending, limit, offset);
        }
        return new GISResponseBuilder<MatchingPois>().build(new MatchingPois(stories, contents, events, tweets,
                shapes, service.getZoneGeometries(zoneIds)));
    }

    @GET
    @Path("/stories")
    public Response getStories(@QueryParam("orderBy") @DefaultValue("last_updated") String orderBy,
                               @QueryParam("isAscending") @DefaultValue("false") boolean isAscending,
                               @QueryParam("limit") @DefaultValue(DEFAULT_LIMIT) int limit,
                               @QueryParam("offset") @DefaultValue("0") int offset,
                               @QueryParam("zoneIds") @DefaultValue("") String zoneIds,
                               @QueryParam("ne") @DefaultValue("") String ne,
                               @QueryParam("sw") @DefaultValue("") String sw,
                               @QueryParam("categoryIds") @DefaultValue("") String categoryIds,
                               @QueryParam("startTime") @DefaultValue("0") long startTime,
                               @QueryParam("endTime") @DefaultValue("0") long endTime,
                               @QueryParam("includeAll") @DefaultValue("false") boolean includeAll) {
        logger.debug("****getStories*Param orderBy:{}, isAscending:{}", orderBy, isAscending);
        logger.debug("****getStories*Param includeAll:{}", includeAll);
        logger.debug("****Param limit:{}, offset:{}", limit, offset);
        List<Story> stories = service.getStories(zoneIds, GeometryUtil.instance.parseEnvelope(ne, sw), categoryIds, startTime, endTime, orderBy, isAscending, limit, offset, includeAll);
        logger.debug("****Records retrieved:{}", stories.size());
        return new GISResponseBuilder<MatchingPois>().build(new MatchingPois(stories, service.getZoneGeometries(zoneIds)));
    }

    @GET
    @Path("/contents")
    public Response getContents(@QueryParam("orderBy") @DefaultValue("pubdate") String orderBy,
                                @QueryParam("isAscending") @DefaultValue("false") boolean isAscending,
                                @QueryParam("limit") @DefaultValue(DEFAULT_LIMIT) int limit,
                                @QueryParam("offset") @DefaultValue("0") int offset,
                                @QueryParam("zoneIds") @DefaultValue("") String zoneIds,
                                @QueryParam("ne") @DefaultValue("") String ne,
                                @QueryParam("sw") @DefaultValue("") String sw,
                                @QueryParam("categoryIds") @DefaultValue("") String categoryIds,
                                @QueryParam("startTime") @DefaultValue("0") long startTime,
                                @QueryParam("endTime") @DefaultValue("0") long endTime) {
        logger.debug("****getContents*Param orderBy:{}, isAscending:{}", orderBy, isAscending);
        logger.debug("****Param limit:{}, offset:{}", limit, offset);
        List<Content> contents = service.getContents(zoneIds, GeometryUtil.instance.parseEnvelope(ne, sw), categoryIds, startTime, endTime, orderBy, isAscending, limit, offset);
        return new GISResponseBuilder<MatchingPois>().build(new MatchingPois(contents, service.getZoneGeometries(zoneIds)));
    }

    @GET
    @Path("/events")
    public Response getEvents(@QueryParam("orderBy") @DefaultValue("pubdate") String orderBy,
                              @QueryParam("isAscending") @DefaultValue("true") boolean isAscending,
                              @QueryParam("limit") @DefaultValue(DEFAULT_LIMIT) int limit,
                              @QueryParam("offset") @DefaultValue("0") int offset,
                              @QueryParam("zoneIds") @DefaultValue("") String zoneIds,
                              @QueryParam("ne") @DefaultValue("") String ne,
                              @QueryParam("sw") @DefaultValue("") String sw,
                              @QueryParam("categoryIds") @DefaultValue("") String categoryIds,
                              @QueryParam("eventSubcategoryIds") @DefaultValue("") String eventSubcategoryIds,
                              @QueryParam("startTime") @DefaultValue("0") long startTime,
                              @QueryParam("endTime") @DefaultValue("0") long endTime) {
        logger.debug("****getEvents*Param orderBy:{}, isAscending:{}", orderBy, isAscending);
        logger.debug("****Param limit:{}, offset:{}", limit, offset);
        List<Event> events = service.getEvents(zoneIds, GeometryUtil.instance.parseEnvelope(ne, sw), categoryIds, eventSubcategoryIds, startTime, endTime, orderBy, isAscending, limit, offset);
        return new GISResponseBuilder<MatchingPois>().build(new MatchingPois(events, service.getZoneGeometries(zoneIds)));
    }

    @GET
    @Path("/tweets")
    public Response getTweets(@QueryParam("orderBy") @DefaultValue("createdat") String orderBy,
                              @QueryParam("isAscending") @DefaultValue("false") boolean isAscending,
                              @QueryParam("limit") @DefaultValue(DEFAULT_LIMIT) int limit,
                              @QueryParam("offset") @DefaultValue("0") int offset,
                              @QueryParam("zoneIds") @DefaultValue("") String zoneIds,
                              @QueryParam("ne") @DefaultValue("") String ne,
                              @QueryParam("sw") @DefaultValue("") String sw,
                              @QueryParam("categoryIds") @DefaultValue("") String categoryIds,
                              @QueryParam("startTime") @DefaultValue("0") long startTime,
                              @QueryParam("endTime") @DefaultValue("0") long endTime) {
        logger.debug("****getTweets*Param orderBy:{}, isAscending:{}", orderBy, isAscending);
        logger.debug("****Param limit:{}, offset:{}", limit, offset);
        List<Tweet> tweets = service.getTweets(zoneIds, GeometryUtil.instance.parseEnvelope(ne, sw), categoryIds, startTime, endTime, orderBy, isAscending, limit, offset);
        return new GISResponseBuilder<MatchingPois>().build(new MatchingPois(tweets, service.getZoneGeometries(zoneIds)));
    }

    @GET
    @Path("/shapes")
    public Response getShapes(@QueryParam("orderBy") @DefaultValue("id") String orderBy,
                              @QueryParam("isAscending") @DefaultValue("true") boolean isAscending,
                              @QueryParam("limit") @DefaultValue(DEFAULT_LIMIT) int limit,
                              @QueryParam("offset") @DefaultValue("0") int offset,
                              @QueryParam("zoneIds") @DefaultValue("") String zoneIds,
                              @QueryParam("ne") @DefaultValue("") String ne,
                              @QueryParam("sw") @DefaultValue("") String sw,
                              @QueryParam("categoryIds") @DefaultValue("") String categoryIds,
                              @QueryParam("shapeTypeIds") @DefaultValue("") String shapeTypeIds) {
        logger.debug("****getShapes*Param orderBy:{}, isAscending:{}", orderBy, isAscending);
        logger.debug("****Param limit:{}, offset:{}", limit, offset);
        List<Shape> shapes = service.getShapes(zoneIds, GeometryUtil.instance.parseEnvelope(ne, sw), shapeTypeIds, categoryIds, orderBy, isAscending, limit, offset);
        return new GISResponseBuilder<MatchingPois>().build(new MatchingPois(shapes, service.getZoneGeometries(zoneIds)));
    }

    @GET
    @Path("/zones")
    public Response getZones() throws CloneNotSupportedException {
        List<Zone> zones = service.getZones();
        List<Zone> descZones = new ArrayList<Zone>(zones.size());
        for (Zone zone : zones) {
            Zone tmp = zone.clone();
            tmp.setTheGeom(null);
            descZones.add(tmp);
        }
        return new GISResponseBuilder<List<Zone>>().build(descZones);
    }

    @GET
    @Path("/sources")
    public Response getSources() throws CloneNotSupportedException {
        List<Source> sources = service.getSources();
        List<Source> descSources = new ArrayList<Source>(sources.size());
        for (Source source : sources) {
            Source tmp = source.clone();
            tmp.setDefaultGeom(null);
            descSources.add(tmp);
        }
        return new GISResponseBuilder<List<Source>>().build(descSources);
    }

    @GET
    @Path("/categories")
    public Response getCategories() {
        return new GISResponseBuilder<List<Category>>().build(service.getCategories());
    }

    @GET
    @Path("/twitterusers")
    public Response getTwitterUsers() {
        return new GISResponseBuilder<List<TwitterUser>>().build(service.getTwitterUsers());
    }

    @GET
    @Path("/eventSubcategories")
    public Response getEventSubcategories() {
        return new GISResponseBuilder<List<EventSubcategory>>().build(service.getEventSubcategories());
    }
}
